"""
@author: chenzhenhua
@project: auto_jift
@file: exam_paper.py
@time: 2021/6/21 0021 15:10
@desc:
"""

import random
import re

import docx
import numpy as np
from docxtpl import DocxTemplate

from config.word.exam_paper import base_data, ever_question_score
from config.word.exam_paper import get_one_info
from config.word.exam_paper import get_question_number


class ToWord(object):
    def __init__(self, question_path):
        self.question_path = question_path

    def read_question(self):
        """
        读取题目

        :return: 所有题目
        """
        file = docx.Document(self.question_path)
        all_question = []
        for para in file.paragraphs:
            all_question.append(para.text)

        all_question = [i for i in all_question if i != '']
        index_judge = all_question.index('一.判断题')
        index_choose = all_question.index('二.单选题')
        index_fill = all_question.index('三.填空题')
        index_short = all_question.index('四.简答题')
        index_calculate = all_question.index('五.计算题')

        judge_all = all_question[index_judge + 1:index_choose]
        choose_all = all_question[index_choose + 1:index_fill]
        fill_all = all_question[index_fill + 1:index_short]
        short_all = all_question[index_short + 1:index_calculate]
        calculate_all = all_question[index_calculate + 1:]
        return judge_all, choose_all, fill_all, short_all, calculate_all

    def split_num_question(self, string):
        """
        分离题目编号与题目

        :param string: 字符串，包括题目和题目编号
        :return: 分离得题目
        """
        return ' '.join(string.split('.')[1:])

    def replace_bracket(self, string):
        """
        将“（）”替换位“_________”

        :param string: 字符串
        :return: “（）”被替换之后得题目
        """
        s = re.sub(r"\（.*?\）", "_________", string)
        return self.split_num_question(s)

    def delete_bracket(self, string):
        """
        删除"（）"内的内容

        :param string: 字符串
        :return: 删除"（）"内的内容
        """
        s = re.sub(r"\（.*?\）", "", string)
        return self.split_num_question(s)

    def get_answer(self, string):
        """
        题目对应的答案，如选择题、填空题、判断题每个题都有对应的答案

        :param string: 字符串
        :return: 题目对应的答案
        """
        return re.findall(r'（(.+?)）', string)

    def get_short_title(self, string):
        """
        获取题号

        :param string: 字符串，题目
        :return: 题号
        """
        return re.findall('(\d+\\.)', string)

    def split_problem_choice(self, choose_question):
        """
        分离选择题题目和选项

        :param choose_question: 选择题
        :return: 选择题题目和选项
        """
        options = []
        question = []
        for choosei in choose_question:
            if (choosei.startswith("A.") or choosei.startswith("B.") or choosei.startswith("C.") or choosei.startswith(
                    "D.")):
                pass
            else:
                question.append(choosei)

        for i in range(len(question) - 1):
            options.append(
                choose_question[choose_question.index(question[i]) + 1:choose_question.index(question[i + 1])])
        options.append(choose_question[choose_question.index(question[len(question) - 1]) + 1:])
        return question, options

    def split_problem_short(self, short_question):
        """
        分离简答题题目和答案

        :param short_question: 简答题题目和答案按word钟顺序排列的集合，一行一个元素
        :return: 简答题题目、答案
        """
        question = []
        answer = []
        index_start = 0

        for short in short_question[:-1]:
            title_num = self.get_short_title(short)
            if title_num:
                question.append(short)
                index_end = short_question.index(short)
                if index_end==index_start:
                    pass
                else:
                    answer.append(short_question[index_start + 1:index_end])
                index_start = index_end
        answer.append(short_question[index_start+1:])
        return question, answer

    def select_question(self, question, answer, num):
        """
        随机分配题目和答案

        :param question: 所有题目，list类型
        :param answer: 所有答案，list类型
        :param num: 随机分配的题目数量
        :return: 分配的题目、答案
        """

        num_list = list(range(len(question)))
        num_question = random.sample(num_list, num)

        questions = np.array(question, dtype=object)[num_question]
        answers = np.array(answer,dtype=object)[num_question]
        return questions, answers

    def exam_question(self):
        """
        随机分配题目

        :return: 选择的题目
        """
        question_num = get_question_number()
        judge_all, choose_all, fill_all, short_all, calculate_all = self.read_question()
        judge_question = random.sample(judge_all, question_num['判断题'])
        question_choose, options = self.split_problem_choice(choose_all)
        question_short, answer_short = self.split_problem_short(short_all)

        choose_question, options = self.select_question(question_choose, options, question_num['选择题'])
        short_question, short_answer = self.select_question(question_short, answer_short, question_num['简答题'])

        fill_question = random.sample(fill_all, question_num['填空题'])

        return judge_question, choose_question, options, fill_question, short_question, short_answer

    def get_questions(self, question, mode):
        """
        选择对应题目和答案

        :param question:所有题目
        :param mode:题型
        :return:选择的题目和对应答案
        """
        questions = []
        answers = []

        for i in range(len(question)):
            question_dict = {}
            s = question[i]
            if mode == 'judge':
                question_dict['question'] = str(i + 1) + '.' + self.delete_bracket(s)
                answers.append(self.get_answer(s)[0])
            elif mode == 'fill':
                question_dict['question'] = str(i + 1) + '.' + self.replace_bracket(s)
                answers.append(self.get_answer(s))
            questions.append(question_dict)
        return questions, answers

    def create_choose_question(self, choose_question, options):
        """
        创建选择题题目

        :param choose_question: 选择题题目
        :param options: 选项
        :return: 所有选择题
        """
        choose_questions = []
        right_answers = []
        for i in range(len(choose_question)):
            choose_question_dict = {}
            s = choose_question[i]

            questioni = self.replace_bracket(s)

            choose_question_dict['question'] = str(i + 1) + '.' + questioni
            choose_question_dict['answer'] = '   '.join(options[i])
            right_answers.append(self.get_answer(s)[0])
            choose_questions.append(choose_question_dict)
        return choose_questions, right_answers

    def create_judge_question(self, judge_question):
        """
        创建判断题题目

        :param judge_question: 判断题题目
        :return: 所有判断题
        """

        return self.get_questions(judge_question, 'judge')

    def create_fill_question(self, fill_question):
        """
        创建填空题题目

        :param fill_question:选择的填空题题目
        :return: 填空题题目、答案
        """

        return self.get_questions(fill_question, 'fill')

    def create_short_question(self, short_question, short_answer):
        """
        将简答题题目和答案写入字典中，方便docxtpl处理

        :param short_question: 简答题题目
        :param short_answer: 简答题答案
        :return: 简答题题目、答案
        """
        short_questions = []
        short_answers = []
        for i in range(len(short_question)):
            short_question_dict = {}
            s = short_question[i]

            questioni = self.split_num_question(s)

            short_question_dict['question'] = str(i + 1) + '.' + questioni
            short_answers.append('\n'.join(short_answer[i]))
            short_questions.append(short_question_dict)
        return short_questions, short_answers

    def create_question(self):
        """
        创建题目列表

        :return: 题目列表
        """
        judge_question, choose_question, options, fill_question, short_question, short_answer = self.exam_question()
        choose_questions, choose_answers = self.create_choose_question(choose_question, options)
        fill_questions, fill_answers = self.create_fill_question(fill_question)
        judge_questions, judge_answers = self.create_judge_question(judge_question)
        short_questions, short_answers = self.create_short_question(short_question, short_answer)

        return \
            choose_questions, fill_questions, judge_questions, short_questions, choose_answers, fill_answers, \
            judge_answers, short_answers

    def base_info(self, subject_name, question1, question2, question3, question4):
        """
        写入内容基本要素

        :param subject_name:课程名
        :param question1: 第一大题
        :param question2: 第二大题
        :param question3: 第三大题
        :param question4: 第四大题
        :return: 写入内容基本信息，字典类型
        """
        subject_info = get_one_info(subject_name)
        subject_info['data1'] = base_data[0]
        subject_info['data2'] = base_data[1]
        subject_info['score1'] = ever_question_score[0]
        subject_info['score2'] = ever_question_score[1]
        subject_info['score3'] = ever_question_score[2]
        subject_info['score4'] = ever_question_score[3]
        subject_info['score_all1'] = ever_question_score[0] * len(question1)
        subject_info['score_all2'] = ever_question_score[1] * len(question2)
        subject_info['score_all3'] = ever_question_score[2] * len(question3)
        subject_info['score_all4'] = ever_question_score[3] * len(question4)

        if base_data[2] == '上':
            subject_info['num'] = '一'
        else:
            subject_info['num'] = '二'
        subject_info['subject_name'] = subject_name

        return subject_info

    def paper_context(self, subject_name, choose_questions, fill_questions, judge_questions, short_questions):
        """
        题目基本信息

        :param subject_name: 课程名
        :param choose_questions: 选择题题目
        :param fill_questions: 填空题题目
        :param judge_questions: 判断题题目
        :param short_questions: 简答题题目
        :return: 题目基本信息
        """

        subject_info = self.base_info(subject_name, choose_questions, fill_questions, judge_questions, short_questions)
        subject_info['choose_questions'] = choose_questions
        subject_info['fill_questions'] = fill_questions
        subject_info['judge_questions'] = judge_questions
        subject_info['short_questions'] = short_questions
        context = subject_info
        return context

    def answer_context(self, subject_name, choose_answers, fill_answers, judge_answers, short_answers):
        """
        答案基本信息

        :param subject_name: 课程名
        :param choose_answers: 选择题答案
        :param fill_answers: 填空题答案
        :param judge_answers: 判断题答案
        :param short_answers: 简答题答案
        :return: 答案基本信息
        """

        subject_info = self.base_info(subject_name, choose_answers, fill_answers, judge_answers, short_answers)

        choose_answers = self.write_col_answer(choose_answers)
        fill_answers = self.write_fill_answer(fill_answers)
        judge_answers = self.write_col_answer(judge_answers)
        short_answers = self.write_short_answer(short_answers)

        subject_info['choose_answers'] = choose_answers
        subject_info['fill_answers'] = fill_answers
        subject_info['judge_answers'] = judge_answers
        subject_info['short_answers'] = short_answers
        context = subject_info
        return context

    def write_col_answer(self, answers):
        """
        写入答案，每行5个，如选择题，判断题

        :param answers:所有题目答案数据
        :return:按固定格式写入参考答案模板中的答案数据
        """
        s = []
        temp = ''
        ins = 1
        for i in range(len(answers)):
            temp = temp + answers[i].replace(' ', '') + ' '
            if i % 5 == 4:
                title_num = str(ins) + '-' + str(ins + 4)
                if len(title_num) < 5:
                    title_num = title_num + ' ' * (5 - len(title_num))
                s.append({'answer': title_num + ' ' + temp})
                ins = ins + 5
                temp = ''
        if len(answers) % 5 != 0:
            title_num = str(ins) + '-' + str(len(answers) % 5)
            if len(title_num) < 5:
                title_num = title_num + ' ' * (5 - len(title_num))
            s.append({'answer': title_num + ' ' + temp})
        return s

    def write_fill_answer(self, fill_answers):
        """
        按填空题格式写入参考答案模板

        :param fill_answers: 填空题所有答案
        :return: 填空题答案
        """
        s = []
        n = len(fill_answers)
        for i in range(n):
            all_answers = ' '.join(fill_answers[i]).strip()
            s.append({'answer': str(i + 1) + '.' + all_answers})
        return s

    def write_short_answer(self, short_answers):
        """
        按简答题格式写入参考答案模板

        :param short_answers: 简答题所有答案
        :return: 简答题答案
        """
        s = []
        n = len(short_answers)

        for i in range(n):
            short_answer = str(i + 1) + short_answers[i]
            s.append({'answer': short_answer})
        return s

    def write_all_info(self, origin_paper_path, origin_answer_path, paper_path, answer_path, subject_name):
        """
        创建试卷和参考答案

        :param origin_paper_path:试卷模板路径
        :param origin_answer_path:试卷答案模板路径
        :param paper_path:导出试卷路径
        :param answer_path:导出参考答案路径
        :param subject_name:课程名
        :return:
        """
        tpl_paper = DocxTemplate(origin_paper_path)
        tpl_answer = DocxTemplate(origin_answer_path)

        choose_questions, fill_questions, judge_questions, short_questions, choose_answers, fill_answers, \
        judge_answers, short_answers = self.create_question()
        context_paper = self.paper_context(subject_name, choose_questions, fill_questions, judge_questions,
                                           short_questions)
        context_answer = self.answer_context(subject_name, choose_answers, fill_answers, judge_answers, short_answers)

        tpl_paper.render(context=context_paper)
        tpl_paper.save(paper_path)

        tpl_answer.render(context=context_answer)
        tpl_answer.save(answer_path)
